package org.jeecg.modules.scm.service.impl;

import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.google.common.base.Charsets;
import com.google.common.hash.Hashing;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.jeecg.common.exception.JeecgBootException;
import org.jeecg.common.util.RedisUtil;
import org.jeecg.common.util.UUIDGenerator;
import org.jeecg.modules.scm.query.FaceAuthQuery;
import org.jeecg.modules.scm.query.H5FaceQuery;
import org.jeecg.modules.scm.service.WxFaceService;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
import org.springframework.web.client.RestTemplate;

import java.net.URLEncoder;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;

@Component
@Slf4j
public class WxFaceServiceImpl implements WxFaceService {

    @Autowired
    private RestTemplate restTemplate;

    @Autowired
    private RedisUtil redisUtil;

    @Value("${wx-face.wb-appid}")
    private String appid;

    @Value("${wx-face.secret}")
    private String secret;

    private static final String accessTokenUrl = "https://miniprogram-kyc.tencentcloudapi.com/api/oauth2/access_token";
    private static final String singTicketUrl = "https://miniprogram-kyc.tencentcloudapi.com/api/oauth2/api_ticket";
    private static final String h5faceUrl = "https://miniprogram-kyc.tencentcloudapi.com/api/server/h5/geth5faceid?orderNo=xxx";
    private static final String nonceTicket = "https://miniprogram-kyc.tencentcloudapi.com/api/oauth2/api_ticket";
    private static final String startH5AuthUrl = "https://miniprogram-kyc.tencentcloudapi.com/api/pc/login";
    private static final String rollbackUrl = "http://http://212.64.69.165:9999/jeecg-scm/wx-face/h5face/rollback/";

    @Override
    public String detectAuth(FaceAuthQuery query) {
        String h5FaceId = getH5FaceId(query);
        log.info("h5FaceId:{}", h5FaceId);
        return startH5Auth(query, h5FaceId);
    }


    /**
     * 启动H5人身核验
     *
     * @param query
     * @param h5FaceId
     */
    public String startH5Auth(FaceAuthQuery query, String h5FaceId) {

        String nonce = UUIDGenerator.generate();
        String sign = getStartH5AuthSign(query, h5FaceId, nonce);
        String url = URLEncoder.encode(rollbackUrl);
        String param = "?webankAppId=" + appid + "&userId=" + query.getUserId() + "&orderNo=" + query.getOrderNo()
                + "&h5faceId=" + h5FaceId + "&nonce=" + nonce + "&sign=" + sign + "&url=" + url + "&version=1.0.0";
        log.info("启动人脸核身请求参数:{}", param);
        log.info(startH5AuthUrl + param);
        return startH5AuthUrl + param;
    }


    /**
     * 获取 accessToken
     *
     * @return
     */
    public String getAccessToken() {
        Object accessToken = redisUtil.get("access-token");
        if (accessToken != null && StringUtils.isNotBlank(accessToken.toString())) {
            return accessToken.toString();
        }
        String param = "?app_id=" + appid + "&secret=" + secret + "&grant_type=client_credential" + "&version=1.0.0";
        String objJson = restTemplate.getForObject(accessTokenUrl + param, String.class);

        JSONObject jsonObject = JSONObject.parseObject(objJson);
        log.info("获取access token:{}", jsonObject.toJSONString());
        if (!jsonObject.get("code").equals("0")) {
            throw new JeecgBootException("获取access token 异常");
        }
        redisUtil.set("access-token", jsonObject.get("access_token").toString(), 20 * 60);
        return jsonObject.get("access_token").toString();
    }

    /**
     * 获取 SingTicket
     *
     * @return
     */
    public String getSingTicket() {
        Object singTicket = redisUtil.get("sing-ticket");
        if (singTicket != null && StringUtils.isNotBlank(singTicket.toString())) {
            return singTicket.toString();
        }
        String accessToken = getAccessToken();
        String param = "?app_id=" + appid + "&access_token=" + accessToken + "&type=SIGN&version=1.0.0";
        String objJson = restTemplate.getForObject(singTicketUrl + param, String.class);
        log.info("获取sing ticket:{}", objJson);
        JSONObject jsonObject = JSONObject.parseObject(objJson);
        if (!jsonObject.get("code").equals("0")) {
            throw new JeecgBootException("获取sing ticket 异常");
        }
        JSONArray ticketsStr = jsonObject.getJSONArray("tickets");

        Object ticketValue = JSONObject.parseObject(ticketsStr.get(0).toString()).get("value");
        redisUtil.set("sing-ticket", ticketValue.toString(), 20 * 60);
        return ticketValue.toString();
    }

    /**
     * 获取 NonceTicke
     *
     * @return
     */
    public String getNonceTicket(String userId) {

        String accessToken = getAccessToken();
        String param = "?app_id=" + appid + "&access_token=" + accessToken + "&type=NONCE&version=1.0.0"
                + "&user_id=" + userId;
        String res = restTemplate.getForObject(nonceTicket + param, String.class);
        log.info("获取nonce ticket:{}", res);
        JSONObject resObject = JSONObject.parseObject(res);
        if (!resObject.get("code").equals("0")) {
            throw new JeecgBootException("获取nonce ticket 异常");
        }
        JSONArray ticketsStr = resObject.getJSONArray("tickets");
        Object ticketValue = JSONObject.parseObject(ticketsStr.get(0).toString()).get("value");
        return ticketValue.toString();
    }

    /**
     * 上传用户信息获取faceId
     *
     * @return
     */
    public String getH5FaceId(FaceAuthQuery query) {
        H5FaceQuery request = new H5FaceQuery();
        BeanUtils.copyProperties(query, request);
        request.setWebankAppId(appid);
        request.setVersion("1.0.0");
        request.setSign(getH5FaceIdSign(query));
        String res = restTemplate.postForObject(h5faceUrl, request, String.class);
        log.info("上传用户信息获取faceId" + res);
        JSONObject resObject = JSONObject.parseObject(res);
        if (!resObject.get("code").equals("0")) {
            throw new JeecgBootException("上传用户信息失败");
        }
        JSONObject resultObject = JSONObject.parseObject(resObject.get("result").toString());
        return resultObject.get("h5faceId").toString();
    }

    /**
     * 上传用户身份信息接口签名
     *
     * @param query
     * @return
     */
    String getH5FaceIdSign(FaceAuthQuery query) {
        List<String> data = new ArrayList<>();
        data.add(query.getName());
        data.add(query.getIdNo());
        data.add(query.getUserId());
        data.add(query.getOrderNo());

        data.add(appid);
        data.add("1.0.0");

        return this.sign(data, getSingTicket());
    }

    /**
     * 启用H5人脸认证接口签名
     *
     * @param query
     * @param h5faceId
     * @param nonce
     * @return
     */
    String getStartH5AuthSign(FaceAuthQuery query, String h5faceId, String nonce) {
        List<String> data = new ArrayList<>();

        data.add(query.getUserId());
        data.add(query.getOrderNo());

        data.add(h5faceId);
        data.add(appid);
        data.add("1.0.0");

        data.add(nonce);
        return this.sign(data, getNonceTicket(query.getUserId()));
    }

    /**
     * 签名
     *
     * @param values
     * @param ticket
     * @return
     */
    public String sign(List<String> values, String ticket) {
        if (values == null) {
            throw new NullPointerException("values is null");
        }
        values.removeAll(Collections.singleton(null));// remove null
        values.add(ticket);
        java.util.Collections.sort(values);
        StringBuilder sb = new StringBuilder();
        for (String s : values) {
            sb.append(s);
        }
        return Hashing.sha1().hashString(sb, Charsets.UTF_8).toString().toUpperCase();
    }


}
